package com.example.featuresdk.repository

import android.util.Log
import androidx.lifecycle.*
import com.example.featuresdk.vo.Resource
import com.example.featuresdk.vo.Status
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import retrofit2.Call
import java.lang.RuntimeException

class ReadAsFlow<T>(
    val fetchLocalData: suspend ()-> Flow<T>,
    val shouldFetchRemoteData: ()->Boolean = { false },
    val fetchRemoteData: suspend () -> Call<Resource<T>>? = { null },
    val saveRemoteData: suspend (data: T) ->Unit = {}
){
    private val TAG = "ReadAsFlow"

    suspend fun execute(): Flow<T> {
        if(shouldFetchRemoteData()) {
            synchronizeRemoteData()
        }
        return fetchLocalData()
    }

    private suspend fun synchronizeRemoteData(){

        runCatching {
            fetchRemoteData()!!.execute()
        }.onSuccess { response ->
            //process the remote data on successful network response
            Log.i(TAG, "response.isSuccessful = ${response.isSuccessful}")

            if (!response.isSuccessful) {//Returns true if code() is in the range [200..300).
                Log.i(TAG, "Error:(${response.message()}):${response.errorBody()}}")
                throw RuntimeException("Error:(${response.message()}):${response.errorBody()}}")
            }

            response.body().let {
                if (it?.data == null) {
                    Log.i(TAG, "response.body() without data, message = ${it?.message}")
                } else {
                    Log.i(TAG, "save data to room")
                    saveRemoteData(it.data)
                }
            }
        }.onFailure {
            //process network exception, e.g. invalid json error
            Log.i(TAG, "Failed to fetch remote data. Error: ${it.toString()}")
            throw RuntimeException("Failed to fetch remote data. Error: ${it.toString()}")
        }
    }
}